#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>

#define KMAX_LEN 64
#define MAX_COM_NUM 2

static int major = 101;
static int minor= 0;
static dev_t devno;
struct mydev {
	struct cdev cdev;
	char *reg;
	int test;
};
struct mydev *pmydev[MAX_COM_NUM];
struct cdev cdev;

static int fifo_open(struct inode *inode, struct file *filep)
{
	struct mydev *cd;

	printk("fifo open() \n");
	// cd = container_of(inode->i_cdev, struct mydev, cdev);
	cd = pmydev[MINOR(inode->i_rdev)];
	filep->private_data = cd;
	return 0;
}

static int fifo_release(struct inode *inode, struct file *filep)
{
	printk("fifo release() \n");
	return 0;
}

static ssize_t fifo_read(struct file *filep, char __user *buf, size_t size, loff_t *pos)
{
	struct mydev *cd;

	printk("fifo read() \n");
	cd = (struct mydev *)filep->private_data;
	printk("cd->test: %d \n", cd->test);

	if(copy_to_user(buf, &(cd->test), size))
		return -EFAULT;

	return size;
}

static struct file_operations fifo_ops =
{
	.open = fifo_open,
	.release = fifo_release,
	.read = fifo_read,
};

static int fifo_init(void)
{
	int error;
	int result = 0;
	int i = 0;

	printk(KERN_ALERT "fifo_init \n");
	devno = MKDEV(major, minor);
	result = register_chrdev_region(devno, MAX_COM_NUM, "test_dev");

	if(result < 0) {
		printk("register chardev region fail \n");
		return result;
	}

	for(i = 0; i < MAX_COM_NUM; i++) {
		pmydev[i] = kmalloc(sizeof(struct mydev), GFP_KERNEL);
		pmydev[i]->test = i;
	}

	cdev_init(&cdev, &fifo_ops);
	devno = MKDEV(major, 0);
	error = cdev_add(&cdev, devno, 2);
	if(error < 0) {
		printk("cdev_add fail \n");
		devno = MKDEV(major, 0);
		unregister_chrdev_region(devno, MAX_COM_NUM);
		for(i = 0; i < MAX_COM_NUM; i++)
			kfree(pmydev[i]);
		return error;
	}

	return 0;
}

static void fifo_exit(void)
{
	int i = 0;

	printk(KERN_ALERT "fifo_exit \n");
	cdev_del(&cdev);
	for(i = 0; i < MAX_COM_NUM; i++)
		kfree(pmydev[i]);
	devno = MKDEV(major, 0);
	unregister_chrdev_region(devno, MAX_COM_NUM);
	return;
}

MODULE_LICENSE("Dual BSD/GPL");

module_init(fifo_init);
module_exit(fifo_exit);
